package org.cjstudio.messageboard;

import java.util.ArrayList;
import java.util.Date;

import org.cjstudio.messageboard.model.Message;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.SortDirection;

interface DAO<T> {
	public abstract boolean create(String key, T anObject);
	public abstract T retrieve(String key);
	public abstract ArrayList<T> retrieve();
	public abstract boolean update(String key, T newObject);
	public abstract boolean delete(String key);
}

public class MessageDAODatastoreImpl implements DAO<Message> {

	private static MessageDAODatastoreImpl messageDAOImpl;
	private static DatastoreService datastore;

	/* This is singleton class with static member! */
	public static MessageDAODatastoreImpl getInstance() {
		if (messageDAOImpl == null) {
			datastore = DatastoreServiceFactory.getDatastoreService();
			return new MessageDAODatastoreImpl();
		} else {
			return messageDAOImpl;
		}
	}

	@Override
	public boolean create(String key, Message message) {
		if (this.retrieve(key) != null) {
			// apply overwrite policy
			this.update(key, message);
			return false;
		} else {
			// if there is no entity matched with the given key
			Entity messageEntity = new Entity(Message.kind, key);
			MessageDAODatastoreImpl.message2entity(messageEntity, message);
			datastore.put(messageEntity);
			return true;
		}
	}

	@Override
	public Message retrieve(String key) {
		try {
			Key messageEntityKey = KeyFactory.createKey(Message.kind, key);
			Entity messageEntity = datastore.get(messageEntityKey);
			return MessageDAODatastoreImpl.messageFromEntity(messageEntity);
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	@Override
	public boolean update(String key, Message newMessage) {
		try {
			Key messageEntityKey = KeyFactory.createKey(Message.kind, key);
			Entity messageEntity = datastore.get(messageEntityKey);
			MessageDAODatastoreImpl.message2entity(messageEntity, newMessage);
			datastore.put(messageEntity);
			return true;
		} catch (EntityNotFoundException e) {
			return false;
		}
	}

	@Override
	public boolean delete(String key) {
		// TODO Auto-generated method stub
		if (this.retrieve(key) != null) {
			Key messageEntityKey = KeyFactory.createKey(Message.kind, key);
			datastore.delete(messageEntityKey);
			return true;
		} else {
			// if there is no entity matched with the given key
			return false;
		}
	}

	@Override
	public ArrayList<Message> retrieve() {
		Query q = new Query(Message.kind);
		q.addSort(Message.pn_date, SortDirection.DESCENDING);
		PreparedQuery pq = datastore.prepare(q);

		ArrayList<Message> list = new ArrayList<Message>();
		for (Entity entity : pq.asIterable()) {
			list.add(MessageDAODatastoreImpl.messageFromEntity(entity));
		}
		return list;
	}

	public static void message2entity(Entity entity, Message message) {
		entity.setProperty(Message.pn_name, message.getName());
		entity.setProperty(Message.pn_phoneNumber, message.getPhoneNumber());
		entity.setProperty(Message.pn_content, message.getContent());
		entity.setProperty(Message.pn_date, message.getDate());
	}

	/* take the property's types into account!! */
	public static Message messageFromEntity(Entity entity) {
		String name = (String) entity.getProperty(Message.pn_name);
		String phoneNumber = (String) entity
				.getProperty(Message.pn_phoneNumber);
		String content = (String) entity.getProperty(Message.pn_content);
		Date date = (Date) entity.getProperty(Message.pn_date);
		return new Message(name, phoneNumber, content, date);
	}
}
